import {
  Controller,
  UseGuards,
  UploadedFiles,
  BadRequestException,
  Post,
  Param,
  Req,
  Delete,
  Get,
} from '@nestjs/common';
import {
  Crud,
  CrudController,
  Override,
  CrudRequest,
  ParsedRequest,
  ParsedBody,
} from '@nestjsx/crud';
import { ApiTags, ApiBearerAuth, ApiConsumes } from '@nestjs/swagger';
import { deserializeArray, deserialize } from 'class-transformer';
import { Song, Album, Artist, ResourceFile } from '../entities';
import { SongsService } from '../services';
import { UserRole } from '@/enum';
import { Roles } from '@/decorators/roles.decorator';
import { AuthGuard, RolesGuard } from '@/guards';
import { FileUtils } from '@/utils';
import { RequestDtoRelationTransformer } from '@/interfaces';
import { User } from '@/modules/users/entities/user.entity';

@Crud({
  model: {
    type: Song,
  },
  query: {
    limit: 25,
    maxLimit: 100,
    join: {
      audioFile: {
        allow: ['mimetype', 'size', 'path'],
      },
      mv: {
        allow: ['mimetype', 'size', 'path'],
      },
      belongsToArtists: {},
      belongsToAlbum: {},
      favByUsers: {},
      inPlayLists: {},
      scores: {},
      tracks: {},
      relatedPosts: {},
    },
  },
  routes: {
    createOneBase: {
      interceptors: [
        FileUtils.multipleFileFieldsUploader(
          [
            { name: 'audioFile', maxCount: 1 },
            { name: 'mv', maxCount: 1 },
          ],
          ['audio', 'video'],
        ),
      ],
    },
    createManyBase: {
      decorators: [
        UseGuards(AuthGuard, RolesGuard),
        Roles(UserRole.ADMIN),
        ApiBearerAuth(),
      ],
    },
    updateOneBase: {
      interceptors: [
        FileUtils.multipleFileFieldsUploader(
          [
            { name: 'audioFile', maxCount: 1 },
            { name: 'mv', maxCount: 1 },
          ],
          ['audio', 'video'],
        ),
      ],
    },
    replaceOneBase: {
      interceptors: [
        FileUtils.multipleFileFieldsUploader(
          [
            { name: 'audioFile', maxCount: 1 },
            { name: 'mv', maxCount: 1 },
          ],
          ['audio', 'video'],
        ),
      ],
    },
    deleteOneBase: {
      decorators: [
        UseGuards(AuthGuard, RolesGuard),
        Roles(UserRole.ADMIN),
        ApiBearerAuth(),
      ],
    },
  },
})
@ApiTags('songs')
@Controller('resources/songs')
// eslint-disable-next-line prettier/prettier
export class SongsController implements CrudController<Song>, RequestDtoRelationTransformer<Song> {
  constructor(public service: SongsService) {}

  get base(): CrudController<Song> {
    return this;
  }

  dtoRelationTransform(dto: Song) {
    try {
      if (typeof dto.belongsToArtists === 'string') {
        dto.belongsToArtists = deserializeArray(Artist, dto.belongsToArtists);
      }
      if (typeof dto.belongsToAlbum === 'string') {
        dto.belongsToAlbum = deserialize(Album, dto.belongsToAlbum);
      }
      if (typeof dto.favByUsers === 'string') {
        dto.favByUsers = deserializeArray(User, dto.favByUsers);
      }
    } catch (e) {
      if (e instanceof SyntaxError) {
        throw new BadRequestException(e.message, e.name);
      }
      throw new BadRequestException();
    }
    return dto;
  }

  private uploadedFilesHandler(dto: Song, files): Song {
    if (files.audioFile) {
      dto.audioFile = new ResourceFile(files.audioFile[0]);
    }
    if (files.mv) {
      dto.mv = new ResourceFile(files.mv[0]);
    }

    return dto;
  }

  /**
   * 查询用户是否喜欢歌曲接口
   * @param id song id
   * @param req request
   */
  @UseGuards(AuthGuard)
  @Get(':id/fav')
  checkFav(@Param() id: number, @Req() req) {
    return this.service.checkFav(id, req.user.id);
  }

  /**
   * 用户喜欢歌曲接口
   * @param id song id
   * @param req request
   */
  @UseGuards(AuthGuard)
  @Post(':id/fav')
  setFav(@Param() id: number, @Req() req) {
    return this.service.setFav(id, req.user.id);
  }

  /**
   * 用户取消喜欢歌曲接口
   * @param id song id
   * @param req request
   */
  @UseGuards(AuthGuard)
  @Delete(':id/fav')
  removeFav(@Param() id: number, @Req() req) {
    return this.service.removeFav(id, req.user.id);
  }

  @UseGuards(AuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth()
  @ApiConsumes('multipart/form-data')
  @Override()
  createOne(
    @ParsedRequest() req: CrudRequest,
    @ParsedBody() dto: Song,
    @UploadedFiles() files,
  ) {
    dto = this.dtoRelationTransform(dto);
    dto = this.uploadedFilesHandler(dto, files);
    return this.base.createOneBase(req, dto);
  }

  @UseGuards(AuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth()
  @ApiConsumes('multipart/form-data')
  @Override()
  updateOne(
    @ParsedRequest() req: CrudRequest,
    @ParsedBody() dto: Song,
    @UploadedFiles() files,
  ) {
    dto = this.dtoRelationTransform(dto);
    dto = this.uploadedFilesHandler(dto, files);
    return this.base.updateOneBase(req, dto);
  }

  @UseGuards(AuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @ApiBearerAuth()
  @ApiConsumes('multipart/form-data')
  @Override()
  replaceOne(
    @ParsedRequest() req: CrudRequest,
    @ParsedBody() dto: Song,
    @UploadedFiles() files,
  ) {
    dto = this.dtoRelationTransform(dto);
    dto = this.uploadedFilesHandler(dto, files);
    return this.base.replaceOneBase(req, dto);
  }
}
